﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace KBEngine.Common
{
    /// <summary>
    /// 日志观察器基类
    /// </summary>
    public class LoggerWatcher
    {
        private TcpClient _socket;
        private MemoryStream _msgBuffer = new MemoryStream();
        private Action<List<string>> _logFunc;
        private string _fireLog;

        /// <summary>
        /// 连接到logger
        /// </summary>
        /// <param name="ip">intaddr</param>
        /// <param name="port">intport</param>
        /// <returns></returns>
        public void connect(IPAddress ip, ushort port)
        {
            _fireLog = $"__{nameof(LoggerWatcher)}_{nameof(__FireLog)}_{Define.getObjAddres(this)}";
            Event.registerIn(_fireLog, this, nameof(__FireLog));
            _socket = new TcpClient();
            _socket.Client.Blocking = true;
            //_socket.ReceiveTimeout = 1000;
            //_socket.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            //_socket.ReceiveTimeout = 1000;
            _socket.Connect(ip, port);
            NetworkStream networkStream = _socket.GetStream();
            Task t2 = Task.Run(async () =>
            {
                try
                {
                    while (true)
                    {
                        if (!networkStream.CanRead)
                            continue;

                        //收到服务器消息
                        do
                        {
                            var readTask = networkStream.ReadAsync(_msgBuffer.data(), _msgBuffer.wpos, (int)_msgBuffer.space());
                            await readTask;
                            _msgBuffer.wpos += readTask.Result;
                            var ms = parseLog();
                            if (ms.Count > 0)
                                Event.fireIn(_fireLog, ms);
                        } while (networkStream.DataAvailable);
                        //sendActiveTick();
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            });
        }

        public void close()
        {
            if (_socket == null)
                return;
            _socket.Close();
            _socket = null;
            Event.deregisterIn(_fireLog, this, nameof(__FireLog));
        }

        /// <summary>
        /// 向logger注册
        /// </summary>
        public void RegisterToLogger(int uid, LogType logType, ICollection<ComponentType> componentTypes, bool isFind, bool isFirst, int globalOrder, int groupOrder, string dateFormat = "", string keyStr = "")
        {
            Bundle bundle = Bundle.createObject();
            bundle.newMessage(FixedMsg.Get("Logger::registerLogWatcher"));
            bundle.writeInt32(uid);
            bundle.writeUint32((uint)logType);
            bundle.writeInt32(globalOrder);
            bundle.writeInt32(groupOrder);
            bundle.writeString(dateFormat);
            bundle.writeString(keyStr);
            bundle.writeInt8((sbyte)componentTypes.Count);
            foreach (var item in componentTypes)
                bundle.writeInt32((int)item);
            bundle.writeInt8((sbyte)(isFind ? 1 : 0));
            bundle.writeInt8((sbyte)(isFirst ? 1 : 0));
            bundle.sendTCP(_socket.Client);
        }
        /// <summary>
        /// 从logger取消注册
        /// </summary>
        public void DeregisterFromLogger()
        {
            Bundle bundle = Bundle.createObject();
            bundle.newMessage(FixedMsg.Get("Logger::deregisterLogWatcher"));
            bundle.sendTCP(_socket.Client);
        }

        /// <summary>
        /// 发送心跳包
        /// </summary>
        public void sendActiveTick()
        {
            Bundle bundle = Bundle.createObject();
            bundle.newMessage(FixedMsg.Get("Logger::onAppActiveTick"));
            bundle.writeInt32((int)ComponentType.console);
            bundle.writeUint64(0);
            bundle.sendTCP(_socket.Client);

        }

        /// <summary>
        /// 向logger发送一条日志
        /// </summary>
        /// <param name="uid"></param>
        /// <param name="logType"></param>
        /// <param name="logStr"></param>
        public void SendLog(int uid, LogType logType, string logStr, ulong componentID = 0, int globalOrder = 0, int groupOrder = 0)
        {
            Bundle bundle = Bundle.createObject();
            bundle.newMessage(FixedMsg.Get("Logger::writeLog"));
            bundle.writeInt32(uid);
            bundle.writeUint32((uint)logType);
            bundle.writeInt32((int)ComponentType.watcher);
            bundle.writeUint64(componentID);
            bundle.writeInt32(globalOrder);
            bundle.writeInt32(groupOrder);
            bundle.writeUint64((ulong)DateTime.Now.Second);
            bundle.writeUint32(0); //kbetime
            bundle.writeString(logStr);
            bundle.sendTCP(_socket.Client);
        }

        /// <summary>
        /// 从数据流中分解日志
        /// </summary>
        private List<string> parseLog()
        {
            List<string> result = new List<string>();
            while (_msgBuffer.length() >= 4)
            {
                var cmdID = _msgBuffer.readUint16();
                var dataLen = _msgBuffer.readUint16();
                if (_msgBuffer.length() < dataLen)
                {
                    _msgBuffer.rpos -= 4;
                    var newBuffer = _msgBuffer.getbuffer();
                    Array.Copy(newBuffer, _msgBuffer.data(), newBuffer.Length);
                    _msgBuffer.rpos = 0;
                    _msgBuffer.wpos = newBuffer.Length;
                    return result;
                }
                if (cmdID != Define.CONSOLE_LOG_MSGID)
                {
                    Logger.Log($"Unknown command.(id = {cmdID})");
                    _msgBuffer.readSkip(dataLen);
                    continue;
                }
                result.Add(_msgBuffer.readUnicode());
            }
            _msgBuffer.clear();
            return result;
        }

        /// <summary>
        /// 接收信息
        /// </summary>
        public void receiveLog(Action<List<string>> callbackFunc, bool loop = false)
        {
            _logFunc = callbackFunc;
        }
        public void __FireLog(List<string> msgList)
        {
            _logFunc?.Invoke(msgList);
        }
    }
}
